#include "RecentContactService_types.h"
#include "RecentContactService.h"

#include <time.h>
#include <boost/scoped_ptr.hpp>
#include <boost/foreach.hpp>

#include "contact_redis.h"
#include "contact_mysql.h"
// #include "contact_thread_manager.h"
#include "add_contact_task.h"

#include "base/logging.h"
#include "base/get_local_ip.h"
#include "base/config_reader.h"
#include "melon/service/service_arch.h"
#include "melon/service/base_service_impl.h"
#include "redis_executor/simple_redis_locator.h"

namespace imserver {

class RecentContactServiceHandler : public RecentContactServiceIf, public melon::service::BaseServiceImpl {

  public:
  RecentContactServiceHandler() : redis_(NULL), mysql_(NULL) {
    ConfigReader config("../conf/recent_contact_service.conf");
    redis_.reset(new ContactRedis(new FixedLocator(config.Get("redis", "server_list").c_str())));
    mysql_.reset(new ContactMySql(
          config.Get("database", "name"), config.Get("database", "server_list"),
          config.Get("database", "user"), config.Get("database", "password"))
        );
  }

  virtual ~RecentContactServiceHandler() {}

  virtual void Add(const int64_t sender, const int64_t receiver) {
    LOG_INFO("[RecentContactServiceHandler::Add] " << sender << '/' <<  receiver);
    GetThreadManager()->add(boost::shared_ptr<Runnable>(new AddContactTask(redis_.get(), mysql_.get(),
            sender, receiver, time(NULL))));
  }

  virtual void Remove(const int64_t sender, const int64_t receiver) {
    LOG_INFO("[RecentContactServiceHandler::Remove] called.");

    redis_->RemoveContact(sender, receiver);
    mysql_->Delete(sender, receiver);
  }

  virtual void GetContacts(::imserver::RecentContactList& _return, const int64_t uid, const int32_t offset, const int32_t count) {
    LOG_INFO("[RecentContactServiceHandler::GetRecentList] called.");

    // redis GetContacts return 1 if exist invalid uid(-1)
    int redis_result = -1;
    for (int i = 0; i < 2; ++i) {
      if (_return.size() > 0) {
        _return.clear();
        LOG_WARN("non-empty _return size=" << _return.size());
      }
      redis_result = redis_->GetContacts(_return, uid, offset, count);
      if (redis_result >= 0) {
        LOG_INFO("GetContacts uid=" << uid << " redis_size=" << _return.size());
        break;
      }
    }

    if (_return.empty() && redis_result == 0) {
      RecentContactList cell;
      mysql_->Select(cell, uid, offset, count);

      // When the user is first login or the user have not done anything, the DB and Redis both would be empty.
      // In order not to read DB-file every time, add an invalid data to the redis as a flag.
      if (cell.empty()) {
        LOG_INFO("First Login User " << uid);
        redis_->Flag(uid);
      } else {
        BOOST_FOREACH(const RecentContact& contact, cell) {
          redis_->AddContact(uid, contact.uid, contact.timestamp);
          _return.push_back(contact);
        }
      }
    }
    LOG_INFO("GetContacts uid=" << uid << " total_size=" << _return.size());
  }

  void AddFavorite(const int64_t sender, const int64_t receiver) {
    // Your implementation goes here
    GetThreadManager()->add(boost::shared_ptr<Runnable>(new AddFavoriteTask(redis_.get(), mysql_.get(),
            sender, receiver, time(NULL))));
  }

  void RemoveFavorite(const int64_t sender, const int64_t receiver) {
    // Your implementation goes here
    redis_->RemoveFavorite(sender, receiver);
  }

  void UpdateRemark(const int64_t sender, const int64_t receiver, const std::string& remark) {
    // Your implementation goes here
    redis_->Remark(sender, receiver, remark);
    mysql_->UpdateRemark(sender, receiver, remark, time(NULL));
  }

  void GetFavorites( ::imserver::RecentContactList& _return, const int64_t uid, const int32_t offset, const int64_t count) {
    // Your implementation goes here
    redis_->GetFavorite(_return, uid, offset, count);
  }

private:
  boost::scoped_ptr<ContactRedis> redis_;
  boost::scoped_ptr<ContactMySql> mysql_;

}; // class RecentContactServiceHandler

}//namespace

int main(int argc, char **argv) {
  ConfigReader cfg("../conf/recent_contact_service.conf");
  LOG_INIT(cfg.Get("log4cplus", "file"), cfg.Get("log4cplus", "level"));
  int port = cfg.GetWithType<int>("recent_contact_service", "port", 9092);
  cout << "listens on port " << port << endl;
  LOG_INFO("listens on port " << port);

  using namespace imserver;
  melon::service::ThriftService<RecentContactServiceHandler, RecentContactServiceProcessor> service;
  service.StartRegister("/im/recent_contact_service", "1.0", 0, base::GetLocalIp(), port, 1);
  service.Start(port);
  return 0;
}
